#pragma once

#include <cstdio>
#include "Protocol.hpp"
#include "TcpSocket.hpp"
#include "UnCopy.hpp"
#include "ThreadPool.hpp"

using task_t = std::function<void()>;

class DlpServer : public UnCopy
{
public:
    DlpServer(uint16_t port) : _port(port)
    {
        init();
    }

    void init()
    {
        _socket = new TcpSocket();
        _socket->createListenSocket(_port);
        ThreadPool<task_t>::getInstance()->start();
        
        factory = std::make_unique<DLP::ReqRespFactory>();
        _map = {
            {'+', [](int x, int y, int& code) -> int {
                code = 0;
                return x + y;
            }},
            {'-', [](int x, int y, int& code) -> int {
                code = 0;
                return x - y;
            }},
            {'*', [](int x, int y, int& code) -> int {
                code = 0;
                return x * y;
            }},
            {'/', [](int x, int y, int& code) -> int {
                if(y == 0) 
                {
                    code = -1;
                    return -1;
                }
                else 
                {
                    code = 0;
                    return x / y;
                }
            }},
            {'%', [](int x, int y, int& code) -> int {
                code = 0;
                return x % y;
            }}
        };
    }

    void start()
    {
        while(true)
        {
            std::string peerIp;
            uint16_t peerPort;
            Socket* communicateSocket = _socket->accept(&peerIp, &peerPort);

            task_t task = std::bind(&DlpServer::processRequest, this, communicateSocket, peerIp, peerPort);
            ThreadPool<task_t>::getInstance()->submit(task);
        }
    }

    void processRequest(Socket* communicateSocket, std::string peerIp, uint16_t peerPort)
    {
        std::string dlpRequestStream = "";
        char dlpRequestBuffer[1024];
        while(true)
        {
            ssize_t n = read(communicateSocket->getSocketFd(), dlpRequestBuffer, sizeof(dlpRequestBuffer)-1);
            if(n > 0)
            {
                std::string content;
                dlpRequestBuffer[n] = '\0';
                dlpRequestStream += dlpRequestBuffer;
                while(DLP::dlpDecoding(dlpRequestStream, content))
                {
                    std::shared_ptr<DLP::Request> req = factory->makeRequest();
                    bool test = req->deserialize(content); // if return false, response 400-bad request
                    if(test == false) std::cout << "req->deserialize false" << std::endl;

                    int code = -1;
                    int result = doCalculate(req->getX(), req->getY(), req->getOp(), code);

                    std::shared_ptr<DLP::Response> resp = factory->makeResponse(result, code);
                    std::string dlpResponse = DLP::dlpEncoding(resp->serialize());

                    printf("[%s:%s] req=%s resp=%s\n", peerIp.c_str(), std::to_string(peerPort).c_str(), content.c_str(), resp->serialize().c_str());
                    write(communicateSocket->getSocketFd(), dlpResponse.c_str(), dlpResponse.size());
                }
            }
            else if(n == 0)
            {
                log.message(INFO, "socket read to the end, client is quit...");
                break;
            }
            else
            {
                log.message(ERROR, "socket read error, %d: %s", errno, strerror(errno));
            }
        }
    }

    int doCalculate(int x, int y, char op, int& code)
    {
        return _map[op](x, y, code);
    }

    ~DlpServer()
    {}
private:
    Socket* _socket;
    uint16_t _port;
    std::unique_ptr<DLP::ReqRespFactory> factory;
    std::unordered_map<char, std::function<int(int, int, int&)>> _map;
};